package com.aiwiown.snackmq.cluster.routing.exchange;

import com.aiwiown.snackmq.cluster.routing.Binding;
import com.aiwiown.snackmq.cluster.routing.Exchange;
import com.aiwiown.snackmq.common.message.Message;
import com.aiwiown.snackmq.common.routing.ExchangeType;
import lombok.Getter;
import lombok.ToString;

import java.util.Collections;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.stream.Collectors;

/**
 * Direct 交换机的实现。
 * 它将消息路由到 routingKey 与消息的 key 完全匹配的队列。
 */
@Getter
@ToString
public class DirectExchange implements Exchange {

    private static final long serialVersionUID = 9068661038098416597L;
    private final String name;
    @ToString.Exclude // 避免在 toString 中产生冗余信息
    private final ExchangeType type = ExchangeType.DIRECT;
    private final List<Binding> bindings = new CopyOnWriteArrayList<>();

    /**
     * 构造一个新的 DirectExchange 实例。
     *
     * @param name 交换机的名称
     */
    public DirectExchange(String name) {
        this.name = name;
    }

    /**
     * 添加一个绑定到交换机。
     * 为避免重复绑定，只有在不存在时才添加。
     *
     * @param binding 要添加的绑定
     */
    @Override
    public void addBinding(Binding binding) {
        if (!this.bindings.contains(binding)) {
            this.bindings.add(binding);
        }
    }

    /**
     * 从交换机中移除一个绑定。
     *
     * @param binding 要移除的绑定
     */
    @Override
    public void removeBinding(Binding binding) {
        this.bindings.remove(binding);
    }

    /**
     * 根据消息的 key 路由消息到相应的队列。
     * 只有当消息的 key 与绑定的 routingKey 完全匹配时，才将消息路由到绑定的队列。
     *
     * @param message 要路由的消息
     * @return 包含队列名称的列表，消息将被路由到这些队列
     */
    @Override
    public List<String> route(Message message) {
        // --- 关键修复：从消息的 'key' 字段获取路由键 ---
        final String messageRoutingKey = message.getKey();

        if (messageRoutingKey == null) {
            return Collections.emptyList();
        }

        // 查找所有 routingKey 与消息的 key 完全匹配的绑定
        return this.bindings.stream()
                .filter(binding -> messageRoutingKey.equals(binding.getRoutingKey()))
                .map(Binding::getQueueName)
                .distinct() // 一个队列可能被多次绑定，但只应接收一次消息
                .collect(Collectors.toList());
    }

    /**
     * 获取交换机的所有绑定。
     * 返回一个不可修改的视图，保护内部列表。
     *
     * @return 包含所有绑定的列表
     */
    @Override
    public List<Binding> getBindings() {
        return Collections.unmodifiableList(this.bindings);
    }
}
